use core::mem::size_of;

use crate::page::Page;

use super::{
    pagedef::{pageshift, pagesize},
    PfnAddr, PhysAddr, VirtKsymAddr, VirtLmAddr,
};

// page 的起始地址
static mut VMEMMAP: usize = 0;

pub fn vmemmap_set(start: usize) {
    unsafe {
        assert_eq!(VMEMMAP, 0);
        VMEMMAP = start;
    }
}

// 整个 page 配置的起始地址, VMEMMAP_START <= VMEMMAP
static mut VMEMMAP_START: usize = 0;

pub fn vmemmap_set_start(start: usize) {
    unsafe {
        assert_eq!(VMEMMAP_START, 0);
        VMEMMAP_START = start;
    }
}

static mut PAGE_OFFSET: usize = 0;

pub fn page_set_offset(offset: usize) {
    unsafe {
        PAGE_OFFSET = offset;
    }
}

static mut KIMAGE_VOFFSET: usize = 0;

pub fn kimage_set_voffset(voffset: usize) {
    unsafe {
        KIMAGE_VOFFSET = voffset;
    }
}

static mut PHYS_OFFSET: usize = 0;

pub fn phys_set_offset(offset: usize) {
    unsafe {
        PHYS_OFFSET = offset;
    }
}

impl Page {
    #[inline(always)]
    pub(super) fn arch_page_to_pfn(&self) -> PfnAddr {
        unsafe {
            assert_ne!(VMEMMAP, 0);
            PfnAddr(self as *const Page as usize - VMEMMAP)
        }
    }

    #[inline(always)]
    fn page_to_voff(&self) -> usize {
        unsafe { (self as *const Page as usize & !VMEMMAP_START) * pagesize() / size_of::<Page>() }
    }

    #[inline(always)]
    pub(super) fn arch_page_to_virtlm(&self) -> VirtLmAddr {
        unsafe { VirtLmAddr(self.page_to_voff() | PAGE_OFFSET) }
    }
}

impl PfnAddr {
    #[inline(always)]
    pub(super) fn arch_pfn_to_page(&self) -> &'static Page {
        unsafe {
            ((VMEMMAP + self.value() * size_of::<Page>()) as *const Page)
                .as_ref()
                .unwrap()
        }
    }

    #[inline(always)]
    pub(super) fn arch_pfn_to_page_mut(&self) -> &'static mut Page {
        unsafe {
            ((VMEMMAP + self.value() * size_of::<Page>()) as *mut Page)
                .as_mut()
                .unwrap()
        }
    }

    #[inline(always)]
    pub(super) fn arch_pfn_to_phys(&self) -> PhysAddr {
        unsafe { PhysAddr(self.value() << pageshift()) }
    }
}

impl PhysAddr {
    #[inline(always)]
    pub(super) fn arch_phys_to_virtksym(&self) -> VirtKsymAddr {
        unsafe { VirtKsymAddr(self.value() + KIMAGE_VOFFSET) }
    }

    #[inline(always)]
    pub(super) fn arch_phys_to_virtlm(&self) -> VirtLmAddr {
        unsafe { VirtLmAddr((self.value() - PHYS_OFFSET) | PAGE_OFFSET) }
    }

    #[inline(always)]
    pub(super) fn arch_phys_to_pfn(&self) -> PfnAddr {
        unsafe { PfnAddr(self.value() >> pageshift()) }
    }

    #[inline(always)]
    pub(crate) fn arch_phys_to_pfn_up(&self) -> PfnAddr {
        unsafe { PfnAddr((self.value() + pagesize() - 1) >> pageshift()) }
    }

    #[inline(always)]
    pub(crate) fn arch_phys_to_pfn_down(&self) -> PfnAddr {
        unsafe { PfnAddr(self.value() >> pageshift()) }
    }
}

impl VirtKsymAddr {
    #[inline(always)]
    pub(super) fn arch_virtksym_to_phys(&self) -> PhysAddr {
        unsafe { PhysAddr(self.value() - KIMAGE_VOFFSET) }
    }
}

impl VirtLmAddr {
    #[inline(always)]
    pub(super) fn arch_virtlm_to_phys(&self) -> PhysAddr {
        unsafe { PhysAddr((self.value() & !PAGE_OFFSET) + PHYS_OFFSET) }
    }

    #[inline(always)]
    fn virtlm_to_pgoff(&self) -> usize {
        unsafe { (self.value() & !PAGE_OFFSET) / pagesize() * size_of::<Page>() }
    }

    #[inline(always)]
    pub(super) fn arch_virtlm_to_page(&self) -> &'static Page {
        unsafe {
            ((self.virtlm_to_pgoff() | VMEMMAP_START) as *const Page)
                .as_ref()
                .unwrap()
        }
    }

    #[inline(always)]
    pub(super) fn arch_virtlm_to_page_mut(&self) -> &'static mut Page {
        unsafe {
            ((self.virtlm_to_pgoff() | VMEMMAP_START) as *mut Page)
                .as_mut()
                .unwrap()
        }
    }
}
